package config

import (
	"fmt"
	"os"
	"reflect"
)

type RendererEnv struct {
	sort int

	bond [][2]string
}

func NewRendererEnv(sort int) *RendererEnv {
	return &RendererEnv{
		sort: sort,
		bond: make([][2]string, 0, 8),
	}
}

func (r *RendererEnv) Name() string {
	return "env"
}

func (r *RendererEnv) Sort() int {
	return r.sort
}

func (r *RendererEnv) Render(body reflect.Value, desc *ConfigDesc) error {
	for _, pair := range r.bond {
		path := pair[0]
		env := pair[1]
		if err := r.parseEnvToPath(body, desc, path, env); err != nil {
			return err
		}
	}

	return nil
}

func (r *RendererEnv) parseEnvToPath(body reflect.Value, desc *ConfigDesc, path, env string) error {
	envVal, ok := os.LookupEnv(env)
	if !ok {
		return nil
	}

	rv, err := findFieldInBody(body, desc, path)
	if err != nil {
		return err
	}
	if rv.Kind() == reflect.Invalid {
		return nil
	}

	if err := setValueFromString(rv, envVal); err != nil {
		return fmt.Errorf("config: set env [%s] value [%s] to [%s] failed: %v", env, envVal, path, err)
	}
	return nil
}

func (r *RendererEnv) BindEnv(path string, env string) {
	for i, pair := range r.bond {
		if pair[0] == path {
			r.bond[i][1] = env
			return
		}
	}

	r.bond = append(r.bond, [2]string{path, env})
}
